Source code for hysop.backend.device.opencl.opencl_allocator

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from abc import ABCMeta, abstractmethod
import numpy as np

from hysop.backend.device.opencl import cl, cl_api
from hysop.core.memory.allocator import AllocatorBase
from hysop.backend.device.opencl.opencl_buffer import OpenClBuffer


[docs] class OpenClAllocator(AllocatorBase, metaclass=ABCMeta): """ Base class for OpenCl backend allocators. """ def __new__(cls, queue, mem_flags=cl.mem_flags.READ_WRITE, verbose=None): return super().__new__(cls, verbose=verbose) def __init__(self, queue, mem_flags=cl.mem_flags.READ_WRITE, verbose=None): super().__init__(verbose=verbose) if queue.device.type == cl.device_type.CPU: # we assume zero copy capabilitiy for CPU devices mem_flags |= cl.mem_flags.ALLOC_HOST_PTR self._queue = queue self._mem_flags = mem_flags if mem_flags & cl.mem_flags.COPY_HOST_PTR: raise ValueError( "pyopencl.mem_flags.COPY_HOST_PTR cannot be passed for an allocator." ) self._max_alloc_size = queue.device.max_mem_alloc_size
[docs] def max_alloc_size(self): """Max allocatable size in bytes.""" return self._max_alloc_size
[docs] def get_queue(self): return self._queue
[docs] def get_context(self): return self._queue.context
[docs] def get_device(self): return self._queue.device
[docs] def get_mem_flags(self): return self._mem_flags
queue = property(get_queue) context = property(get_context) device = property(get_device) mem_flags = property(get_mem_flags)
[docs] def allocate(self, nbytes, **kwds): """ Wraps _allocate_impl to raise an MemoryError if a cl.Error with the is_out_of_memory flag. Should return an hysop.core.memory.buffer.OpenClBuffer """ super().allocate(nbytes=nbytes, **kwds) try: return self._allocate_impl(nbytes=nbytes) except cl.Error as e: raise MemoryError(str(e))
[docs] def is_on_host(self): """ Return true if buffers are allocated in host memory. """ return self.device.type == cl.device_type.CPU
@abstractmethod def _allocate_impl(nbytes): pass
[docs] def prefix(self): return f"{self.full_tag}: "
[docs] class OpenClDeferredAllocator(OpenClAllocator): """ Deferred OpenCL allocator, memory is allocated when used on device. """ is_deferred = True def _allocate_impl(self, nbytes): assert isinstance(nbytes, int) return OpenClBuffer(context=self.context, mem_flags=self.mem_flags, size=nbytes)
[docs] class OpenClImmediateAllocator(OpenClAllocator): """ Immediate OpenCL allocator, memory allocation is not deferred. Convenient for memory pools and to catch allocation errors early. """ is_deferred = False _zero = np.array([0, 0, 0, 0], dtype=np.int8) def _allocate_impl(self, nbytes): assert isinstance(nbytes, int) buf = OpenClBuffer(context=self.context, mem_flags=self.mem_flags, size=nbytes) try: cl_api._enqueue_write_buffer( self.queue, buf, self._zero[: min(len(self._zero), nbytes)], is_blocking=True, ) except RuntimeError as e: # pyopencl will throw a RuntimeError (invalid argument) # if buffer could not be allocated raise MemoryError(str(e)) return buf
[docs] def memory_pool(self, name, **kwds): """ Construct a memory pool from this allocator. """ from hysop.backend.device.opencl.opencl_mempool import ( MemoryPool, OpenClMemoryPool, ) if isinstance(self, MemoryPool): msg = "allocator is already a memory pool." raise RuntimeError(msg) return OpenClMemoryPool(allocator=self, name=name, verbose=None, **kwds)